# encoding: utf-8

import socket
import errno
import sys
import Queue

tamanio_mensaje = 1024
max_cola_tam = 20 # maxima cantidad de mensajes a enviar almacenados

class Conexion_cliente(object):
	def __init__(self,socket):
		self.socket = socket
		self.buffer = Buffer()
		self.cola_salida = Queue.Queue(max_cola_tam)
		# Ya sea a causa de un error o a pedido de el cliente, si luego de enviar
		# el mensaje que tenemos en la cola de salida de este cliente, terminar_conexion
		# es True => debemos cerrar la conexión.
		self.terminar_conexion = False
		#Los 3 siguientes campos se usan para la función get_slice
		self.offset = None #offset
		self.length = None #longitud del pedazo
		self.f_name = None #nombre del archivo del cual obtener el pedazo
		self.num_pet_inc = 0 #numero de peticiones incorrectas hechas
		
	"""	Ejecuta la primitiva recv sobre el socket self.socket.
	PRE : {self.socket es un descriptor de socket válido and self.socket está
	conectado}
	mensaje = recibir_mensaje()
	POST : {mensaje != None or (mensaje == None and el cliente se desconectó)}	"""
	def recibir_mensaje(self, esperar, tiempo_espera):
		# PRECONDICIÓN
		assert isinstance(self.socket, socket.socket)
		# (esperar == True )=> se necesita que definamos tiempo_espera como
		# límite de tiempo a la operación recv.  
		if esperar:
			self.socket.settimeout(tiempo_espera)
	
		try:
			mensaje = self.socket.recv(tamanio_mensaje)
			# Suponemos que nunca pueden ocurrir los errores ENOTSOCK (por 
			# precondición), EFAULT e EINVAL (por correcta implementación de
			# librerías de Python).
		except socket.error , descripcion :
			codigo_error = descripcion[0]
			sys.stderr.write("¡Problemas al intentar recibir un mensaje!: %s" % descripcion)
			mensaje = None
			if codigo_error == errno.EBADF:
				# Quizás el socket se ha cerrado y ya no es un descriptor de
				#conector válido.
				mensaje = None
			elif codigo_error == errno.ECONNREFUSED:
				# Seguir estudiando cuando puede ocurrir este error.
				mensaje = None
			elif codigo_error == errno.EINTR:
				# Seguir estudiando cuando puede ocurrir este error. El socket
				# estaba bloqueado en una recepción y lo hemos interrumpido
				# antes de que hubiera algún dato disponible.
				mensaje = None
			elif codigo_error == errno.ENOTCONN:
				# El usuario se desconectó.
				mensaje = None
		except socket.timeout:
			sys.stderr.write("¡Expiró el tiempo de espera!")
			mensaje = None

		return mensaje
	
	""" Envia 'mensaje' a traves del socket self.socket. Bloquea hasta que 
		 termina de enviar todo. 
	PRE : {self.socket es un descriptor de socket válido and self.socket está
	conectado and len(mensaje) <= tamanio_mensaje}
	codigo_error = enviar_mensaje(mensaje)
	POST : {(codigo_error == 0 and mensaje se envió correctamente) or 
	(codigo_error < 0 and no se pudo enviar mensaje)} """
	
	def enviar_mensaje(self):
		# PRECONDICIÓN
		#assert (!self.cola_salida.empty())
		try:	
			mensaje = self.cola_salida.get(False) #si no hay nada NO se bloquea
		except Queue.Empty: #no hay nada para enviar!
			sys.stderr.write("No hay nada para enviar! No se hace nada\n")
			codigo_error = -1
			return codigo_error
		
		codigo_error = 0
		tam = len(mensaje)
		tam_enviado = 0
		while tam_enviado < tam:
			# Cortar, de mensaje, lo que falta enviar
			porcion_restante = mensaje[int(tam_enviado):]
			try:
				tam_enviado = self.socket.send(porcion_restante)
			except socket.error , descripcion:
				codigo_error = descripcion[0]
				# Suponemos que nunca pueden ocurrir EBADF, ENOTSOCK, EWOULDBLOCK,
				# ENOTCONN
				sys.stderr.write("¡Problemas al intentar enviar un mensaje!: %s" % descripcion)
				if codigo_error == errno.EINTR:
					# Reintentamos
					self.enviar_mensaje(mensaje[porcion_restante:])
				elif codigo_error == errno.EPIPE:
					codigo_error = -1
				elif codigo_error == errno.EWOULDBLOCK:
					codigo_error = -1
					# Salimos del ciclo
				tam_enviado = tam
		
		return codigo_error
	
	"""	Ejecuta la primitiva close sobre el descriptor de socket self.socket.	
	PRE : {self.socket es un descriptor de socket válido and self.socket está 
	conectado}
	POST : {el descriptor self.socket se ha cerrado}
	"""
	def cerrar_conexion(self):
		try:
			self.socket.close()
		except socket.error , descripcion :
			codigo_error = descripcion[0]
			# Suponemos que nunca puede ocurrir EBADF
			sys.stderr.write("¡Problemas!: %s" % descripcion)
			if codigo_error == errno.EINTR:
				# Reintentamos
				self.cerrar_conexion()
			elif codigo_error == errno.EIO:
				# Reintentamos
				self.cerrar_conexion()
				
	def encolar_mensaje(self, mensaje):
		""" Guarda mensaje en la cola de salida del objeto conexión cliente. Si la
		cola ya está llena no hace nada y retorna -1. En caso de éxito encola
		el mensaje y devuelve 0.
		"""
		try:
			self.cola_salida.put(mensaje,False) #que no se bloquee si está lleno
		except Queue.Full:
			sys.stderr.write("Cola de salida llena no se guarda el mensaje\n")
			return -1
		
		return 0
	
	def set_offset_length_fname(self, new_offset, new_length, file_name):
		""" Setea los valores self.offset, self.length y self.f_name """
		self.offset = new_offset
		self.length = new_length
		self.f_name = file_name
		return 0
	
	def get_offset(self):
		""" Devuelve self.offset"""
		return self.offset
	def get_length(self):
		""" Imaginese lo que hace """
		return self.length
	def get_fname(self):
		""" ¿Hace falta explicar? """
		return self.f_name
	
	def incr_cant_peticiones_incorrectas(self):
		""" Incrementa en 1 la cantidad de peticiones incorrectas """
		self.num_pet_inc += 1
		
	def get_cant_peticiones_incorrectas(self):
		""" Devuelve la cantidad de peticiones incorrectas """
		return self.num_pet_inc

	def hay_respuestas(self):
		""" Devuelve true si la cola de salida no esta vacia """
		hay = not self.cola_salida.empty()
		return hay

	def set_terminar_conexion (self, estado):
		self.terminar_conexion = estado

	def get_terminar_conexion (self):
		return self.terminar_conexion

class Buffer(object):
	def __init__(self):
		self.mensaje = ""
	
	""" 
	PRE : {mensaje es un string and b == B and isinstance(B,Buffer)} 
	b.almacenar_mensaje(mensaje)
	POST : {b.devolver_mensaje() == B.devolver_mensaje()+mensaje} 
	"""
	def almacenar_mensaje(self,mensaje):
		self.mensaje += mensaje
		assert self.mensaje == self.mensaje[0:-len(mensaje)]+mensaje
	
	""" Devuelve una copia del mensaje almacenado en el buffer """	
	def devolver_mensaje(self):
		return str(self.mensaje)
		
	def isEmpty(self):
		return self.mensaje == ""
	
	def vaciar(self):
		self.mensaje = ""
